package com.gxa.j259.springcloud.f.client.controller;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ServerEndpoint 注解主要的作用是将当前类定义成一个websocket服务器端,在此ServerEndpoint
 * Javax.websocket包则包含服务端和客户端公用的注解，类，接口，异常
 * Javax.websocket.server包含注解，类，接口用于创建和配置服务端点
 * WebSocket是JavaEE7新支持的  ★★★
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 * 在js中通过本地连接   var websocket = new WebSocket("ws://localhost:8080/websocket");
 * 或者通过IP地址       var websocket = new WebSocket("ws://172.16.0.5:8080/websocket");
 */
@Component
@ServerEndpoint("/websocket")
public class WebSocketUtil {
    // 静态变量，用来记录当前在线连接数。AtomicInteger增量器是线程安全的。
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    // CopyOnWriteArraySet是线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<>();

    // 客户端的连接会话，需要通过它给客户端发送数据
    private Session session;

    public WebSocketUtil() {
        System.out.println("-----------WebSocketTest构造器---------------");
    }

    public WebSocketUtil(Session session) {
        this.session = session;
    }

    /**
     * webSocket连接建立成功时会调用此方法
     * @param session  可选的参数。session是当前访问的客户端会话，在连接时保存它，为后面发送消息做准备
     */
    @OnOpen
    public void onOpen(Session session){
        // 此session不是http session，它是websocket session   session的主要目的为了发送消息
        this.session = session;
        System.out.println("session.getId()=" + session.getId());
        webSocketSet.add(this);     // 把此客户端对象加入到线程安全的CopyOnWriteArraySet中
        onMessage("有新连接加入！当前在线人数为" + addOnlineCount(), null);
        System.out.println("有新连接加入！当前在线人数为" + addOnlineCount()); //在线数加1
    }

    /**
     * webSocket关闭时调用的方法
     */
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  // 把此客户端对象从线程安全的CopyOnWriteArraySet中移除
        System.out.println("有一连接关闭！当前在线人数为" + subOnlineCount()); //在线数减1
    }

    /**
     * 收到任意客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 当前客户端的session对象
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
        //群发消息  公告和新闻
        for(WebSocketUtil item: webSocketSet){
            // 当给某一个不存在的客户端发送消息，会抛出异常，所以需要try起来
            // 这种不存在的客户端会有两种情况发生，第一种：用户退出页面，但是服务器没有检查到退出
            //                                    第二种：向指定用户发送消息，但是此用户已经退出页面
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发生错误时调用
     * @param session  出错的客户端session
     * @param error    错误状态对象
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 发送消息的方法，主要调用session中的方法发送消息
     * @param message        发送的消息内容
     * @throws IOException   如果发送失败会抛出异常
     */
    private void sendMessage(String message) throws IOException{
        // java 中 webstocket发送消息的核心方法
        this.session.getBasicRemote().sendText(message);
    }

    private int addOnlineCount() {
        return WebSocketUtil.onlineCount.incrementAndGet();   // incrementAndGet把当前个数+1，然后返回此总个数
    }

    private int subOnlineCount() {
        return WebSocketUtil.onlineCount.decrementAndGet();   // decrementAndGet把当前个数-1，然后返回此总个数
    }
}